#include <SDL3/SDL_events.h>
#include <ranges>
#include <vector>

#define SDL_MAIN_USE_CALLBACKS

#include <SDL3/SDL_error.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_video.h>
#include <SDL3/SDL_vulkan.h>

#define SPDLOG_USE_STD_FORMAT
#include <spdlog/spdlog.h>

#define VK_NO_PROTOTYPES
#include "header/vulkan_utility.hpp"
#include <volk/volk.h>

#define val const auto
val appInfo = VkApplicationInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
                                .pApplicationName = "Vulkan Demo",
                                .applicationVersion = 0x00,
                                .pEngineName = "No Engine",
                                .engineVersion = 0x00,
                                .apiVersion = VK_API_VERSION_1_0};
std::vector<const char *> instantLayers{};
namespace {
SDL_Window *window{nullptr};
VkInstance instance{VK_NULL_HANDLE};
} // namespace
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
  if (!SDL_Init(SDL_INIT_VIDEO)) {
    spdlog::critical("Fail to init SDL3");
    return SDL_APP_FAILURE;
  }

  if (val res = volkInitialize(); res != VK_SUCCESS) {
    spdlog::critical("Fail to init volk, error: {}", res);
    return SDL_APP_FAILURE;
  }

  window = SDL_CreateWindow("Vulkan app", 800, 600,
                            SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
  if (!window) {
    spdlog::critical("Fail to create window, error: {}", SDL_GetError());
    return SDL_APP_FAILURE;
  }

  unsigned extensionCount;
  val extensionList = SDL_Vulkan_GetInstanceExtensions(&extensionCount);
  spdlog::info("Avaliable extension count: {}", extensionCount);
  for (val i : std::views::iota(0u, extensionCount)) {
    spdlog::info("{}", extensionList[i]);
  }

  val instanceCreateInfo = VkInstanceCreateInfo{
      .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
#if __APPLE__
      .flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR,
#else
      .flags = 0,
#endif
      .pApplicationInfo = &appInfo,
      .enabledLayerCount =
          static_cast<uint32_t>(std::ranges::size(instantLayers)),
      .ppEnabledLayerNames = instantLayers.data(),
      .enabledExtensionCount = extensionCount,
      .ppEnabledExtensionNames = extensionList};
  if (val result = vkCreateInstance(&instanceCreateInfo, nullptr, &instance);
      result != VK_SUCCESS) {
    spdlog::critical("Fail to create instance, error: {}", result);
    return SDL_APP_FAILURE;
  }

  return SDL_APP_CONTINUE;
}

SDL_AppResult SDL_AppIterate(void *appstate) { return SDL_APP_CONTINUE; }
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
  switch (event->type) {
  case SDL_EVENT_QUIT:
    return SDL_APP_SUCCESS;
    break;
  default:
    return SDL_APP_CONTINUE;
  }
}
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
  if (instance != VK_NULL_HANDLE) {
    vkDestroyInstance(instance, nullptr);
  }
  if (window != nullptr) {
    SDL_DestroyWindow(window);
  }
}